package com.smart.validate;

import com.smart.validate.an.*;
import com.smart.validate.core.ValidateCache;
import com.smart.validate.core.ValidateHandler;
import com.smart.validate.core.ValidateResult;
import com.smart.validate.exception.ParamsException;
import com.smart.validate.exception.Strategy;
import com.smart.validate.exception.ValidateErrorListener;
import com.smart.validate.utils.CommonUtil;
import com.smart.validate.utils.ReflectUtils;
import com.smart.validate.utils.Tuple2;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.ServiceLoader;
import java.util.Set;
import java.util.function.Function;

/**
 * @author weaponry MrYe
 * @program smart-validator
 * @description 轻量级参数校验器服务
 * <p>
 * 基于fetch.smart.validate改进
 * 增加分组校验
 * 增加所有条件校验完成后统一返回
 * 去除不必要的第三方依赖
 * 增加校验失败时自定义ExceptionSPI扩展
 * </p>
 * @created 2020-07-19 18:43
 */
public class SmartValidator {

    private static final Logger log = LoggerFactory.getLogger(SmartValidator.class);

    private Object value;

    private Config config;

    public enum Config {
        ALL,
        ONE
    }

    private SmartValidator(Object value) {
        this.value = value;
    }

    /**
     * 新建校验实例，传入目标对象
     *
     * @param value 校验对象
     * @return ValidateUtils
     */
    public static SmartValidator is(Object value) {
        return new SmartValidator(value);
    }

    /**
     * 切换目标对象，不重新创建实例
     *
     * @param value 校验对象
     * @return ValidateUtils
     */
    public SmartValidator and(Object value) {
        this.value = value;
        return this;
    }

    /**
     * 接收value对象
     * <p>
     * 通常运用于依赖数据库校验
     * </p>
     *
     * @return ValidateUtils
     */
    public Tuple2<SmartValidator, Object> map(Function function) {
        return new Tuple2<>(this, ValidateHandler.map(function, value, (e, err) -> wrapError(e, err)));
    }

    /**
     * 非空校验
     *
     * @return ValidateUtils
     */
    public SmartValidator notNull() {
        return notNull(null);
    }

    /**
     * 非空校验
     *
     * @param msg 错误信息
     * @return ValidateUtils
     */
    public SmartValidator notNull(String msg) {
        ValidateHandler.notNull(value, msg, (e, err) -> wrapError(e, err));
        return this;
    }

    /**
     * 正则校验
     *
     * @param regex 正则表达式
     * @return ValidateUtils
     */
    public SmartValidator regex(String regex) {
        return regex(regex, null);
    }

    /**
     * 正则校验
     *
     * @param regex 正则表达式
     * @param msg   错误信息
     * @return ValidateUtils
     */
    public SmartValidator regex(String regex, String msg) {
        ValidateHandler.regex(regex, value, msg, (e, err) -> wrapError(e, err));
        return this;
    }

    /**
     * 最大值校验
     *
     * @param max 最大值
     * @return ValidateUtils
     */
    public SmartValidator max(Number max) {
        return max(max, null);
    }

    /**
     * 最大值校验
     *
     * @param max 最大值
     * @param msg 错误信息
     * @return ValidateUtils
     */
    public SmartValidator max(Number max, String msg) {
        ValidateHandler.max(max, value, msg, (e, err) -> wrapError(e, err));
        return this;
    }

    /**
     * 最小值校验
     *
     * @param min 最小值
     * @return ValidateUtils
     */
    public SmartValidator min(Number min) {
        return min(min, null);
    }

    /**
     * 最小值校验
     *
     * @param min 最小值
     * @param msg 错误信息
     * @return ValidateUtils
     */
    public SmartValidator min(Number min, String msg) {
        ValidateHandler.min(min, value, msg, (e, err) -> wrapError(e, err));
        return this;
    }

    /**
     * 最大长度校验
     *
     * @param max 最大长度
     * @return ValidateUtils
     */
    public SmartValidator maxLength(int max) {
        return maxLength(max, null);
    }

    /**
     * 最大长度校验
     *
     * @param max 最大长度
     * @param msg 错误信息
     * @return ValidateUtils
     */
    public SmartValidator maxLength(int max, String msg) {
        ValidateHandler.maxLength(max, value, msg, (e, err) -> wrapError(e, err));
        return this;
    }

    /**
     * 最小长度校验
     *
     * @param min 最小长度
     * @return ValidateUtils
     */
    public SmartValidator minLength(int min) {
        return minLength(min, null);
    }

    /**
     * 最小长度校验
     *
     * @param min 最小长度
     * @param msg 错误信息
     * @return ValidateUtils
     */
    public SmartValidator minLength(int min, String msg) {
        ValidateHandler.minLength(min, value, msg, (e, err) -> wrapError(e, err));
        return this;
    }

    /**
     * 中文校验
     *
     * @return ValidateUtils
     */
    public SmartValidator chinese() {
        return chinese(null);
    }

    /**
     * 中文校验
     *
     * @param msg 错误信息
     * @return ValidateUtils
     */
    public SmartValidator chinese(String msg) {
        ValidateHandler.chinese(value, msg, (e, err) -> wrapError(e, err));
        return this;
    }

    /**
     * 英文校验
     *
     * @return ValidateUtils
     */
    public SmartValidator english() {
        return english(null);
    }

    /**
     * 英文校验
     *
     * @param msg 错误信息
     * @return ValidateUtils
     */
    public SmartValidator english(String msg) {
        ValidateHandler.english(value, msg, (e, err) -> wrapError(e, err));
        return this;
    }

    /**
     * 手机号校验
     *
     * @return ValidateUtils
     */
    public SmartValidator phone() {
        return phone(null);
    }

    /**
     * 手机号校验
     *
     * @param msg 错误信息
     * @return ValidateUtils
     */
    public SmartValidator phone(String msg) {
        ValidateHandler.phone(value, msg, (e, err) -> wrapError(e, err));
        return this;
    }

    /**
     * 邮箱校验
     *
     * @return ValidateUtils
     */
    public SmartValidator email() {
        return email(null);
    }

    /**
     * 邮箱校验
     *
     * @param msg 错误信息
     * @return ValidateUtils
     */
    public SmartValidator email(String msg) {
        ValidateHandler.email(value, msg, (e, err) -> wrapError(e, err));
        return this;
    }

    /**
     * 自定义日期格式校验
     *
     * @param format 格式
     * @return ValidateUtils
     */
    public SmartValidator date(String format) {
        return date(format, null);
    }

    /**
     * 自定义日期格式校验
     *
     * @param format 格式
     * @param msg    错误信息
     * @return ValidateUtils
     */
    public SmartValidator date(String format, String msg) {
        ValidateHandler.date(format, value, msg, (e, err) -> wrapError(e, err));
        return this;
    }

    /**
     * 身份证校验
     *
     * @return ValidateUtils
     */
    public SmartValidator idCard() {
        return idCard(null);
    }

    /**
     * 身份证校验
     *
     * @param msg 错误信息
     * @return ValidateUtils
     */
    public SmartValidator idCard(String msg) {
        ValidateHandler.idCard(value, msg, new ValidateErrorListener() {
            @Override
            public void handler(ParamsException e, String msg) {
                wrapError(e, msg);
            }
        });
        return this;
    }

    /**
     * IP地址校验
     *
     * @return ValidateUtils
     */
    public SmartValidator ip() {
        return ip(null);
    }

    /**
     * IP地址校验
     *
     * @param msg 错误信息
     * @return ValidateUtils
     */
    public SmartValidator ip(String msg) {
        ValidateHandler.ip(value, msg, new ValidateErrorListener() {
            @Override
            public void handler(ParamsException e, String msg) {
                wrapError(e, msg);
            }
        });
        return this;
    }

    /**
     * 对象校验（通过注解）
     *
     * @param value 校验对象
     * @return ValidateUtils
     */
    public static SmartValidator check(Object value) {
        return check(value, null, Config.ONE);
    }

    public static SmartValidator check(Object value, Set<String> groups) {
        return check(value, groups, Config.ONE);
    }

    public static SmartValidator check(Object value, Config config) {
        return check(value, null, config);
    }

    public static SmartValidator check(Object value, Set<String> groups, Config config) {
        SmartValidator smartValidator = new SmartValidator(value);
        smartValidator.config = config;
        smartValidator.notNull();
        Class classType = value.getClass();
        Set<Field> fieldSet = ValidateCache.getInstance().getFieldsByClass(classType);
        if (null == fieldSet) {
            fieldSet = ReflectUtils.getFieldsByClass(value.getClass());
            ValidateCache.getInstance().setClassFields(classType, fieldSet);
        }
        if (CommonUtil.isNull(fieldSet)) {
            return smartValidator;
        }
        long start = System.currentTimeMillis();
        Set<Field> filterFields = groupFields(fieldSet, groups);
        log.debug("ValidateUtils check group cost:{}ms", System.currentTimeMillis() - start);

        for (Field field : filterFields) {
            Annotation[] annotations = ValidateCache.getInstance().getAnnotationsByField(field);
            if (CommonUtil.isNull(annotations)) {
                annotations = field.getAnnotations();
                if (CommonUtil.isNull(annotations)) {
                    continue;
                }
                ValidateCache.getInstance().setFieldAnnotations(field, annotations);
            }
            Object fieldValue;
            try {
                boolean accessible = field.isAccessible();
                //打开私有访问
                if (!accessible) {
                    field.setAccessible(true);
                }
                //获取属性值
                fieldValue = field.get(value);
                if (!accessible) {
                    field.setAccessible(false);
                }
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
            for (Annotation annotation : annotations) {
                if (annotation instanceof NotNull) {
                    smartValidator.and(fieldValue).notNull(((NotNull) annotation).msg());
                } else if (annotation instanceof Max) {
                    Max max = (Max) annotation;
                    smartValidator.and(fieldValue).max(max.value(), max.msg());
                } else if (annotation instanceof Min) {
                    Min min = (Min) annotation;
                    smartValidator.and(fieldValue).min(min.value(), min.msg());
                } else if (annotation instanceof MaxLength) {
                    MaxLength maxLength = (MaxLength) annotation;
                    smartValidator.and(fieldValue).maxLength(maxLength.value(), maxLength.msg());
                } else if (annotation instanceof MinLength) {
                    MinLength minLength = (MinLength) annotation;
                    smartValidator.and(fieldValue).minLength(minLength.value(), minLength.msg());
                } else if (annotation instanceof Email) {
                    smartValidator.and(fieldValue).email(((Email) annotation).msg());
                } else if (annotation instanceof Phone) {
                    smartValidator.and(fieldValue).phone(((Phone) annotation).msg());
                } else if (annotation instanceof IdCard) {
                    smartValidator.and(fieldValue).idCard(((IdCard) annotation).msg());
                } else if (annotation instanceof Regex) {
                    Regex regex = (Regex) annotation;
                    smartValidator.and(fieldValue).regex(regex.value(), regex.msg());
                } else if (annotation instanceof Date) {
                    Date date = (Date) annotation;
                    String format = date.format();
                    smartValidator.and(fieldValue).date(format, date.msg());
                } else if (annotation instanceof Chinese) {
                    smartValidator.and(fieldValue).chinese(((Chinese) annotation).msg());
                } else if (annotation instanceof English) {
                    smartValidator.and(fieldValue).english(((English) annotation).msg());
                } else if (annotation instanceof IP) {
                    smartValidator.and(fieldValue).ip(((IP) annotation).msg());
                }
            }
        }
        return smartValidator;
    }

    /**
     * 增加分组校验
     * <p>
     * 扩展
     * </p>
     *
     * @param sourceFields
     * @param groups
     * @return
     */
    private static Set<Field> groupFields(Set<Field> sourceFields, Set<String> groups) {
        // 过滤出含分组注解的字段
        if (CommonUtil.isNull(groups)) {
            return sourceFields;
        }
        Set<Field> groupFields = new HashSet<>();
        for (Field field : sourceFields) {
            Annotation[] annotations = ValidateCache.getInstance().getAnnotationsByField(field);
            if (CommonUtil.isNull(annotations)) {
                annotations = field.getAnnotations();
                if (!CommonUtil.isNull(annotations)) {
                    ValidateCache.getInstance().setFieldAnnotations(field, annotations);
                }
            }
            for (Annotation annotation : annotations) {
                if (annotation instanceof Group) {
                    List<String> names = Arrays.asList(((Group) annotation).names());
                    if (groups.stream().anyMatch(group -> names.contains(group))) {
                        groupFields.add(field);
                    }
                }
            }
        }
        return groupFields;
    }

    private ValidateResult validateResult = new ValidateResult();

    private void wrapError(ParamsException e, String msg) {
        validateResult.setSuccess(false);
        String errMsg = CommonUtil.defaultIfBlank(msg, e.getMessage());
        if (Config.ALL.equals(config)) {
            validateResult.getErrors().add(errMsg);
        } else {
            ServiceLoader<Strategy> exceptionLoader = ServiceLoader.load(Strategy.class);
            throw exceptionLoader.iterator().next().getException(errMsg);
        }
    }

    public ValidateResult getResult() {
        return validateResult;
    }
}
